#pragma once
#include "../common/message.hpp"
#include "../common/net.hpp"
#include "requestor.hpp"

namespace myrpc {    
    namespace client {
        class Provider {
        public:
            using ptr = std::shared_ptr<Provider>;
            Provider(const Requestor::ptr &requestor):_requestor(requestor){}

            bool registerMethod(const BaseConnection::ptr &conn, const std::string &method, const Address &host){
                auto svr_req = MessageFactory::create<ServiceRequest>();
                svr_req->setMsgId(UuId::uuId());
                svr_req->setMsgType(MsgType::REQ_SERVICE);
                svr_req->setServiceOptype(ServiceOpType::SERVICE_REGISTRY);
                svr_req->setMethod(method);
                svr_req->setHost(host);
                BaseMessage::ptr msg;
                bool ret = _requestor->send(conn, svr_req, msg);
                if (ret == false){
                    client_logger->ERROR("服务 '%s' 注册请求发送失败!", method.c_str());
                    return false;
                }
                ServiceResponse::ptr svr_rsp = std::dynamic_pointer_cast<ServiceResponse>(msg);
                if (svr_rsp.get() == nullptr){
                    client_logger->ERROR("服务 '%s' 注册响应消息转换失败!", method.c_str());
                    return false;
                }
                if (svr_rsp->getRCode() != RCode::RCODE_OK){
                    client_logger->ERROR("服务 '%s' 注册失败! 原因: %s", method.c_str(), errReason(svr_rsp->getRCode()).c_str());
                    return false;
                }
                return true;
            }

        private:
            Requestor::ptr _requestor;
        };

        class MethodHost {
        public:
            using ptr = std::shared_ptr<MethodHost>;
            MethodHost(){}
            MethodHost(const std::vector<Address> &hosts):_hosts(hosts){}

            void addHost(const Address &host){
                std::unique_lock<std::mutex> lock(_mutex);
                _hosts.push_back(host);
            }
            void removeHost(const Address &host){
                std::unique_lock<std::mutex> lock(_mutex);
                for (auto it = _hosts.begin(); it != _hosts.end(); it++){
                    if (*it == host){
                        _hosts.erase(it);
                        break;
                    }
                }
            }
            Address chooseHost(){
                std::unique_lock<std::mutex> lock(_mutex);
                size_t pos = (_idx++) % _hosts.size();
                return _hosts[pos];
            }
            bool empty(){
                return _hosts.empty();
            }

        private:
            std::mutex _mutex;
            size_t _idx = 0;
            // 能提供服务的对应的主机信息 采用轮转数组的形式实现负载均衡
            std::vector<Address> _hosts;
        };

        class Discoverer {
        public:
            using ptr = std::shared_ptr<Discoverer>;
            using CloseClientCB = std::function<void(const Address &)>;
            Discoverer(const Requestor::ptr &requestor, const CloseClientCB &cb)
            : _requestor(requestor),
            _close_client_cb(cb)
            {}
            bool serviceDiscover(const BaseConnection::ptr &conn, const std::string &method, Address &host){
                // 先查找发现的目标服务是否已有保存的主机信息
                {
                    std::unique_lock<std::mutex> lock(_mutex);
                    auto it = _method_hosts.find(method);
                    if (it != _method_hosts.end()){
                        if (!it->second->empty()){
                            host = it->second->chooseHost();
                            client_logger->DEBUG("服务发现 '%s': 从本地缓存中选择主机 %s:%d", method.c_str(), host.first.c_str(), host.second);
                            return true;   // 注意找到主机后返回的是true
                        }
                    }
                }
                client_logger->DEBUG("服务发现 '%s': 本地无缓存, 向注册中心请求", method.c_str());
                // 不存在就向服务中心发送服务发现请求
                auto svr_req = MessageFactory::create<ServiceRequest>();
                svr_req->setMsgId(UuId::uuId());
                svr_req->setMsgType(MsgType::REQ_SERVICE);
                svr_req->setServiceOptype(ServiceOpType::SERVICE_DISCOVERY);
                svr_req->setMethod(method);
                BaseMessage::ptr msg;
                bool ret = _requestor->send(conn, svr_req, msg);
                if (ret == false){
                    client_logger->ERROR("服务发现 '%s' 请求发送失败!", method.c_str());
                    return false;
                }
                auto svr_rsp=std::dynamic_pointer_cast<ServiceResponse>(msg);
                if (svr_rsp.get() == nullptr){
                    client_logger->ERROR("服务发现 '%s' 响应消息转换失败!", method.c_str());
                    return false;
                }
                if (svr_rsp->getRCode() != RCode::RCODE_OK){
                    client_logger->ERROR("服务发现 '%s' 失败! 原因: %s", method.c_str(), errReason(svr_rsp->getRCode()).c_str());
                    return false;
                }
                // 从响应中获取主机信息并建立新的映射关系保存
                std::unique_lock<std::mutex> lock(_mutex);
                auto method_host = std::make_shared<MethodHost>(svr_rsp->getHosts());
                if (method_host->empty()){
                    client_logger->ERROR("服务发现 '%s' 成功, 但未返回任何可用的服务主机!", method.c_str());
                    return false;
                }
                host = method_host->chooseHost();
                _method_hosts[method] = method_host;
                client_logger->INFO("服务发现 '%s' 成功, 缓存 %zu 个主机, 本次选择: %s:%d", method.c_str(), svr_rsp->getHosts().size(), host.first.c_str(), host.second);
                return true;
            }

            // 服务请求操作 服务上线与下线的回调处理——注册到Dispatcher事件分发器中
            void onServiceResquest(const BaseConnection::ptr &conn, ServiceRequest::ptr &svr_req){
                auto optype = svr_req->getServiceOpType();
                std::string method = svr_req->getMethod();
                auto host = svr_req->getHost();
                std::unique_lock<std::mutex> lock(_mutex);
                // 1.服务上线请求 找到对应的管理主机的对象 添加新的主机信息
                if (optype == ServiceOpType::SERVICE_ONLINE){
                    client_logger->INFO("服务上线通知: '%s' @ %s:%d", method.c_str(), host.first.c_str(), host.second);
                    auto it = _method_hosts.find(method);
                    if (it == _method_hosts.end()){
                        // 没有找到 就创建新的对象 建立新的映射关系
                        auto method_host=std::make_shared<MethodHost>();
                        method_host->addHost(svr_req->getHost());
                        _method_hosts.insert(std::make_pair(method, method_host));
                        return;
                    }
                    // 找到直接添加
                    it->second->addHost(svr_req->getHost());
                }
                // 2.服务下线请求 找到管理主机的对象 从中删除对应的主机信息
                else if (optype == ServiceOpType::SERVICE_OFFLINE){
                    client_logger->INFO("服务下线通知: '%s' @ %s:%d", method.c_str(), host.first.c_str(), host.second);
                    auto it = _method_hosts.find(method);
                    if (it != _method_hosts.end()){
                        it->second->removeHost(svr_req->getHost());
                    }
                    _close_client_cb(svr_req->getHost());
                }
            }

        private:
            std::mutex _mutex;
            std::unordered_map<std::string, MethodHost::ptr> _method_hosts;
            Requestor::ptr _requestor;
            CloseClientCB _close_client_cb;
        };
    }
}